﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Reflection.Emit;

using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swcommands;
using SolidWorks.Interop.swconst;
using SolidWorks.Interop.swpublished;

using SwAddinHelpers.Contracts;
using SwAddinHelpers.Attributes;

namespace SwAddinHelpers.CommandManager
{
    //http://www.angelsix.com/cms/tutorials/64-solidworks/74-solidworks-menus-a-toolbars

    public sealed class CommandManagerHost: AddinProxy
    {
        private ICommandManager _CommandManager;
        private List<ICommandGroup> _CommandGroups;
        private int uid = 100;

        public CommandManagerHost(ISwAddinBase owner)
        {
            _OwnerAddin = owner;
        }

        private ICommandGroup FindGroupByName(string name)
        {
            return _CommandGroups.Find(
                delegate(ICommandGroup e)
                { return e.Name.ToUpper() == name.ToUpper() ; });
        }

        public void Build()
        {
            _CommandGroups = new List<ICommandGroup>();
            
            _CommandManager = _OwnerAddin.SolidWorksApplication.GetCommandManager(_OwnerAddin.Cookie);

            IEnumerable<IExternalCallback> externalCallbackContainers = 
                _OwnerAddin.compositionContainer.GetExportedValues<IExternalCallback>();

            foreach (IExternalCallback callbackContainer in externalCallbackContainers)
            {
                CommandGroupDefinitionAttribute[] cgda = (CommandGroupDefinitionAttribute[])callbackContainer.GetType().GetCustomAttributes(typeof(CommandGroupDefinitionAttribute), false);

                if (cgda.Length > 0)
                {
                    foreach (CommandGroupDefinitionAttribute cgd in cgda)
                    {
                        ICommandGroup cg = FindGroupByName(cgd.groupName);

                        if (cg == null)
                        {
                            _CommandGroups.Add(_CommandManager.CreateCommandGroup(_CommandGroups.Count, cgd.groupName, cgd.groupName, cgd.groupName, -1));
                            cg = FindGroupByName(cgd.groupName);
                            cg.HasMenu = cgd.hasMenu;
                            cg.HasToolbar = cgd.hasToolbar;
                            cg.ShowInDocumentType = cgd.documentTypesAsInt; // TODO: seems to not have any effect
                            if (cgd.bmpImageStripPath != null && System.IO.File.Exists(cgd.bmpImageStripPath))
                            {
                                cg.SmallIconList = cgd.bmpImageStripPath;
                                cg.LargeIconList = cgd.bmpImageStripPath;
                            }
                        }
                    }
                }

                MethodInfo[] mis = callbackContainer.GetType().GetMethods();
                foreach (MethodInfo mi in mis)
                {
                     CommandItemCallbackAttribute[] cica = 
                         (CommandItemCallbackAttribute[])mi.GetCustomAttributes(typeof(CommandItemCallbackAttribute), false);

                     if (cica.Length > 0)
                     {
                         _OwnerAddin.CallbackPool.InjectCommandProxyMethod(mi);

                         if (cica[0].commandItemEnableMethod != null)
                         {
                             MethodInfo enableMi = callbackContainer.GetType().GetMethod(cica[0].commandItemEnableMethod);
                             if (enableMi != null)
                             {
                                 _OwnerAddin.CallbackPool.InjectCommandProxyMethod(enableMi);
                             }
                         }

                         ICommandGroup cg = FindGroupByName(cica[0].commandGroupName);

                         if (cg == null)
                         {
                           // command item cannot be added because its group has not been defined
                         }
                         else
                         {
                             int cmdType = 0;

                             if (cica[0].isMenuItem)
                                 cmdType = (int)swCommandItemType_e.swMenuItem;

                             if (cica[0].isToolbarItem)
                                 cmdType |= (int)swCommandItemType_e.swToolbarItem;

                             
                             cg.AddCommandItem2(cica[0].commandItemName, uid, cica[0].commandItemToolTip, cica[0].commandItemToolTip, cica[0].imageIndex, mi.Name, cica[0].commandItemEnableMethod, uid, cmdType);
                             
                             uid++;
                         }
                         
                     }
                }
            }

            //_CommandGroup.Activate(); <-- Moved to PostBuild since we need a callback target to call Activate()
        }

        public void PostBuild()
        {
            foreach (ICommandGroup cg in _CommandGroups)
            {
               cg.Activate();
            }
        }

        public void Destroy()
        {
            for (int i = 0; i < _CommandGroups.Count; i++)
            {
                _CommandManager.RemoveCommandGroup(i);
                System.Runtime.InteropServices.Marshal.ReleaseComObject(_CommandGroups[i]);
                _CommandGroups[i] = null;
            }

            _CommandGroups.Clear();

            System.Runtime.InteropServices.Marshal.ReleaseComObject(_CommandManager);
            _CommandManager = null;
        }
    }
}